package com.example.csetpp.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.csetpp.exception.GetAllUsersException;
import com.example.csetpp.exception.OssFileOperateException;
import com.example.csetpp.exception.UnregisteredException;
import com.example.csetpp.mapper.FileMapper;
import com.example.csetpp.mapper.UserMapper;
import com.example.csetpp.pojo.dto.PageUserDTO;
import com.example.csetpp.pojo.dto.file.*;
import com.example.csetpp.pojo.entity.File;
import com.example.csetpp.pojo.entity.User;
import com.example.csetpp.pojo.vo.file.*;
import com.example.csetpp.service.FileService;
import com.example.csetpp.utils.AliOSSUtils;
import com.example.csetpp.utils.FileParserUtil;
import com.example.csetpp.utils.SnowFlakeID;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Locale;

@Service
@Transactional
public class FileServiceImpl implements FileService {
    @Autowired
    private AliOSSUtils aliOSSUtils;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 上传头像
     * @param image
     * @param userId
     * @return
     * @throws Exception
     */
    @Override
    public String postAvatar(MultipartFile image, String userId) throws Exception {
        User user = userMapper.selectById(userId);
        if(user == null){
            throw new UnregisteredException("用户不存在！");
        }
        String url = aliOSSUtils.upload(image);
//        String fileName = url.substring(url.lastIndexOf("/") + 1);
        if(user.getAvatar() != null){
            String oldUrl = user.getAvatar();
            String oldFileName = oldUrl.substring(oldUrl.lastIndexOf("/") + 1);
            deleteFile(oldUrl,oldFileName);
        }
        user.setAvatar(url);
        userMapper.updateById(user);
        return url;
    }

    @Override
    public String deleteFile(String url, String fileName) throws Exception {
        aliOSSUtils.deleteFile(fileName,0);
        return "OK";
    }
    @Override
    public Object stockPredict(StockDTO stockDTO) {
        if(stockDTO.getStart_date() == null){
            stockDTO.setStart_date(LocalDate.now().toString());
        }
        String key1 = "stock:" + stockDTO.getStart_date() + ":"  + "ICBC";
        String key2 = "stock:" + stockDTO.getStart_date() + ":"  + "CATL";
        String key3 = "stock:" + stockDTO.getStart_date() + ":"  + "Moutai";
        String redisResult =  stringRedisTemplate.opsForValue().get(key1);
        JSONArray jsonArray1 = getRedisData(redisResult);
        redisResult = stringRedisTemplate.opsForValue().get(key2);
        JSONArray jsonArray2 = getRedisData(redisResult);
        redisResult = stringRedisTemplate.opsForValue().get(key3);
        JSONArray jsonArray3 = getRedisData(redisResult);
        return new StockVO(jsonArray1,jsonArray2,jsonArray3);
    }

    @Override
    public Object electricityPredict(StaticPredictDTO staticPredictDTO) {
        if(staticPredictDTO.getStart_date() == null){
            staticPredictDTO.setStart_date(LocalDate.now().toString());
        }
        String key = "electricity:" + staticPredictDTO.getStart_date();
        String redisResult =  stringRedisTemplate.opsForValue().get(key);
        JSONArray jsonArray = getRedisData(redisResult);
        return jsonArray;
    }

    /**
     * 0 - 用户 1 - 后台
     * @param staticPredictDTO
     * @param type
     * @return
     */
    @Override
    public Object staticWeatherPredict(StaticPredictDTO staticPredictDTO, Integer type) {
        if(staticPredictDTO.getStart_date() == null){
            staticPredictDTO.setStart_date(LocalDate.now().toString());
        }
        String key1 = "weather:" + staticPredictDTO.getStart_date();
        String key2 = "weather:" + staticPredictDTO.getStart_date() + ":" + "hour";
        String redisResult1 =  stringRedisTemplate.opsForValue().get(key1);
        String redisResult2 =  stringRedisTemplate.opsForValue().get(key2);
        JSONArray jsonArray1 = getRedisData(redisResult1);
        JSONArray jsonArray2 = getRedisData(redisResult2);
        return staticWeatherDetail(jsonArray2,jsonArray1,type);
    }


    public StaticWeatherVO staticWeatherDetail(JSONArray jsonArray1,JSONArray jsonArray2,Integer type){
        int currentHour = LocalDateTime.now().getHour();
        if(type == 0){
            JSONObject hourData = jsonArray1.getJSONObject(currentHour);
            StaticWeatherVO staticWeatherVO = new StaticWeatherVO();
            staticWeatherVO.setNow(hourData);
            staticWeatherVO.setMonthly(jsonArray2);
            return staticWeatherVO;
        }
        StaticWeatherVO staticWeatherVO = new StaticWeatherVO();
        staticWeatherVO.setDaily(jsonArray1);
        staticWeatherVO.setMonthly(jsonArray2);
        return  staticWeatherVO;
    }

    @Override
    public Object submitWeatherPredict(MultipartFile file, String userId,Integer time_step) throws IOException {
        User user = userMapper.selectById(userId);
        if(user == null){
            throw new GetAllUsersException("用户不存在！");
        }
        String url = "http://8.138.247.36:5000/predict/weather/custom";
        Object aimJson =  FileParserUtil.parseFile(file);
        SubmitPredictDTO submitPredictDTO = new SubmitPredictDTO(time_step,aimJson);
        ResponseBean responseBean = null;
        try {
            responseBean = restTemplate.postForObject(url, submitPredictDTO, ResponseBean.class);
        } catch (RestClientException e) {
            throw new OssFileOperateException("所传文件数据格式错误！");
        }
        String sourceFile = aliOSSUtils.upload(file);
        MultipartFile multipartFile = FileParserUtil.saveJsonToFile(responseBean.getData(),"expired");
        String predictFile = aliOSSUtils.upload(multipartFile);
        File aimFile = new File(SnowFlakeID.getSnowflakeId(),userId,
                sourceFile,predictFile,System.currentTimeMillis());
        fileMapper.insert(aimFile);
        return responseBean.getData();
    }

    @Override
    public Object getFileRecord(PageUserDTO pageUserDTO) {
        User user = userMapper.selectById(pageUserDTO.getUserId());
        if(user == null){
            throw new GetAllUsersException("用户不存在！");
        }
        Page<File> page = new Page<>(pageUserDTO.getPage(),pageUserDTO.getSize());
        QueryWrapper<File> queryWrapper =new QueryWrapper<>();
        queryWrapper.eq("customer_id",pageUserDTO.getUserId());
        queryWrapper.orderByDesc("request_time");
        page = fileMapper.selectPage(page,queryWrapper);
        return page.getRecords();
    }

    @Override
    public Object deleteUserFile(String id) throws Exception {
        System.out.println(id);
        File file = fileMapper.selectById(id);
        if(file == null){
            throw new OssFileOperateException("文件记录不存在！！");
        }
        String sourceFile = file.getSourceFile();
        String predictFile = file.getPredictResult();
        fileMapper.deleteById(id);
        aliOSSUtils.deleteFile(sourceFile,1);
        aliOSSUtils.deleteFile(predictFile,2);
        return "OK";
    }

    @Override
    public Object UserPredictionSearch(UserPredictionSearchDTO userPredictionSearchDTO) {
        Page<File> page = new Page<>(userPredictionSearchDTO.getPage(),userPredictionSearchDTO.getSize());
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(userPredictionSearchDTO.getUserId() != null,"customer_id",userPredictionSearchDTO.getUserId())
                .ge(userPredictionSearchDTO.getStartDate() != null, "request_time",userPredictionSearchDTO.getStartDate())
                .le(userPredictionSearchDTO.getEndDate() != null,"request_time",userPredictionSearchDTO.getEndDate());
        queryWrapper.orderByDesc("request_time");
        Long i = fileMapper.selectCount(queryWrapper);
        page = fileMapper.selectPage(page,queryWrapper);
        FileSearchVO fileSearchVO = new FileSearchVO(i,page.getRecords());
        return fileSearchVO;
    }

    @Override
    public Object getModulePrediction(ModulePredictionDTO modulePredictionDTO) {
        String type = modulePredictionDTO.getType();
        if(type == null){
            throw new OssFileOperateException("类型不能为空！");
        }
        Long date = modulePredictionDTO.getDate();
        LocalDate localDate = LocalDate.now();
        if(date != null){
            localDate = Instant.ofEpochMilli(date)
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
        } else {
            date = localDate.atStartOfDay(ZoneId.systemDefault())
                    .toInstant()
                    .toEpochMilli();
        }
        Object aimResult = null;
        if(type.equals("stock")){
            StockDTO stockDTO = new StockDTO(localDate.toString(),30, "stock");
            aimResult = stockPredict(stockDTO);
        } else if(type.equals("electricity")){
            StaticPredictDTO staticPredictDTO = new StaticPredictDTO(localDate.toString(),30);
            aimResult = electricityPredict(staticPredictDTO);
        } else if(type.equals("weather")){
            StaticPredictDTO staticPredictDTO = new StaticPredictDTO(localDate.toString(),30);
            aimResult = staticWeatherPredict(staticPredictDTO,1);
        }
        if(aimResult == null){
            throw new OssFileOperateException("模型类型选择出错,没有传入正确类型！");
        }
        String key = type + ":" + localDate.toString();
        ModulePredictionVO modulePredictionVO = new ModulePredictionVO(key,date,aimResult);
        return modulePredictionVO;
    }

    @Override
    public void predictByDate(ModulePredictionDTO modulePredictionDTO) {
        Long date = modulePredictionDTO.getDate();
        String type = modulePredictionDTO.getType();
        if(date == null || type == null){
            throw new OssFileOperateException("参数缺失！");
        }
        LocalDate localDate = Instant.ofEpochMilli(date)
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        if(type.equals("all")){
            StockDTO stockDTO = new StockDTO(localDate.toString(),30,"ICBC");
            stockPredictTask(stockDTO);
            stockDTO.setSub_type("CATL");
            stockPredictTask(stockDTO);
            stockDTO.setSub_type("Moutai");
            stockPredictTask(stockDTO);
            StaticPredictDTO staticPredictDTO = new StaticPredictDTO(localDate.toString(),30);
            electricityPredictTask(staticPredictDTO);
            staticWeatherPredictTask(staticPredictDTO);
            staticHourWeatherPredictTask(staticPredictDTO);
        } else if(type.equals("stock")){
            StockDTO stockDTO = new StockDTO(localDate.toString(),30,"ICBC");
            stockPredictTask(stockDTO);
            stockDTO.setSub_type("CATL");
            stockPredictTask(stockDTO);
            stockDTO.setSub_type("Moutai");
            stockPredictTask(stockDTO);
        } else if(type.equals("electricity")){
            StaticPredictDTO staticPredictDTO = new StaticPredictDTO(localDate.toString(),30);
            electricityPredictTask(staticPredictDTO);
        } else if(type.equals("weather")){
            StaticPredictDTO staticPredictDTO = new StaticPredictDTO(localDate.toString(),30);
            staticWeatherPredictTask(staticPredictDTO);
            staticHourWeatherPredictTask(staticPredictDTO);
        }
    }

    @Override
    public void deletePredictionByKey(KeyDTO keyDTO) {
        String key = keyDTO.getKey();
        if(key == null){
            throw new OssFileOperateException("key为空");
        }
        int colonIndex = keyDTO.getKey().indexOf(':');
        String result;
        if (colonIndex != -1) {
            result = key.substring(0, colonIndex); // 截取开头到冒号前的部分
        } else {
            result = key; // 没有冒号时返回整个字符串
        }
        if(result.equals("stock")){
            stringRedisTemplate.delete(key + ":ICBC");
            stringRedisTemplate.delete(key + ":CATL");
            stringRedisTemplate.delete(key + ":Moutai");
            return;
        } else if(result.equals("weather")){
            stringRedisTemplate.delete(key);
            stringRedisTemplate.delete(key + ":hour");
        }

    }

    @Scheduled(cron = "0 0 8 * * ?") // 每天上午8点执行
    public void taskWithCron() {
        StockDTO stockDTO = new StockDTO(LocalDate.now().toString(),30,"ICBC");
        stockPredictTask(stockDTO);
        stockDTO.setSub_type("CATL");
        stockPredictTask(stockDTO);
        stockDTO.setSub_type("Moutai");
        stockPredictTask(stockDTO);
        StaticPredictDTO staticPredictDTO = new StaticPredictDTO(LocalDate.now().toString(),30);
        electricityPredictTask(staticPredictDTO);
        staticWeatherPredictTask(staticPredictDTO);
        staticHourWeatherPredictTask(staticPredictDTO);
    }

    private JSONArray getRedisData(String aimData){
        if(aimData == null || aimData.isBlank()){
            throw new OssFileOperateException("无预测数据！");
        }
        JSONArray jsonArray = JSONArray.parseArray(aimData);
        return jsonArray;
    }

    public void stockPredictTask(StockDTO stockDTO) {
        String url = "http://8.138.247.36:5000/predict/stock";
        if(stockDTO.getStart_date() == null){
            stockDTO.setStart_date(LocalDate.now().toString());
        }
        String stockKey = "stock:" + stockDTO.getStart_date() + ":"  + stockDTO.getSub_type();
        ResponseBean responseBean = null;
        try {
            responseBean = restTemplate.postForObject(url, stockDTO, ResponseBean.class);
        } catch (RestClientException e) {
            throw new OssFileOperateException("调用模型接口出错！");
        }
        stringRedisTemplate.opsForValue().set(stockKey,JSON.toJSONString(responseBean.getData()));
    }

    public void electricityPredictTask(StaticPredictDTO staticPredictDTO) {
        String url = "http://8.138.247.36:5000/predict/electricity";
        if(staticPredictDTO.getStart_date() == null){
            staticPredictDTO.setStart_date(LocalDate.now().toString());
        }
        String key = "electricity:" + staticPredictDTO.getStart_date();
        ResponseBean responseBean = null;
        try {
            responseBean = restTemplate.postForObject(url, staticPredictDTO, ResponseBean.class);
        } catch (RestClientException e) {
            throw new OssFileOperateException("调用模型接口出错！");
        }
        stringRedisTemplate.opsForValue().set(key,JSON.toJSONString(responseBean.getData()));
    }

    public void staticWeatherPredictTask(StaticPredictDTO staticPredictDTO) {
        String url = "http://8.138.247.36:5000/predict/weather/default";
        if(staticPredictDTO.getStart_date() == null){
            staticPredictDTO.setStart_date(LocalDate.now().toString());
        }
        String key = "weather:" + staticPredictDTO.getStart_date();

        ResponseBean responseBean = null;
        try {
            responseBean = restTemplate.postForObject(url, staticPredictDTO, ResponseBean.class);
        } catch (RestClientException e) {
            throw new OssFileOperateException("调用模型接口出错！");
        }
        stringRedisTemplate.opsForValue().set(key,JSON.toJSONString(responseBean.getData()));
    }

    public void staticHourWeatherPredictTask(StaticPredictDTO staticPredictDTO) {
        String url = "http://8.138.247.36:5000/predict/weather/default/detailed";
        String key = "weather:" + staticPredictDTO.getStart_date() + ":" + "hour" ;
        ResponseBean responseBean = null;
        try {
            responseBean = restTemplate.postForObject(url, staticPredictDTO, ResponseBean.class);
        } catch (RestClientException e) {
            throw new OssFileOperateException("调用模型接口出错！");
        }
        stringRedisTemplate.opsForValue().set(key,JSON.toJSONString(responseBean.getData()));
    }

}
